#include "ggt_impl.h"

//ggt_impl::ggt_impl(){
//
//}

ggt_impl::ggt_impl(int argc, char *argv[], CORBA::Object_var nso, int sleep,
		int pid, int timeout) {
	char *PIDName = new char[5/*slave*/ + 5/*00000<-ID*/ + 1/*\0*/+4/*.log*/];
	sprintf(PIDName, "slave%5d.log", pid);
	this->logger.open(PIDName);
	this->logger << "---------------------------------------------" << endl;
	this->logger << "  GGT Started " << endl;
	this->logger << "---------------------------------------------" << endl;

	gotResponseWithOne = false;
	kill = false;
	initiatedQuery = false;
	processTimeout = timeout;
	nsobj = nso;
	int retry = 10;
	bereit = false;
	rechnen = false;
	myPIDisMyName = pid;
	gotContact();

	sleepingtime = sleep;
	//########################################################################
	//Loesung zu Punkt 20
	//########################################################################
	while (retry != 0) {
		try {
			nc = CosNaming::NamingContext::_narrow (nsobj);
			name.length (1);
			name[0].id = CORBA::string_dup ("chef");
			name[0].kind = CORBA::string_dup ("");
			obj = nc->resolve( name);
			bigBoss = chef::koordinator::_narrow( obj);

			this->logger << " Koordinator resolved" << endl;

			//################################################################
			//Loesung zu Punkt 21
			//################################################################
			bereit = true;
			mi = 666; //Defaultwert
			retry = 0;

		} catch(CORBA::ORB::InvalidName_catch& ex)
		{
			this->logger << " InvalidName Exception " << endl;
			cerr << "1\n";
			retry--;
		}
		catch(CosNaming::NamingContext::NotFound_catch& ex)
		{
			this->logger << " NotFound Exception " << endl;
			cerr << "2\n";
			retry--;
		}
		catch(CORBA::SystemException_catch& ex)
		{
			this->logger << "System Exception " << endl;
			cout << "3\n";
			retry--;
		}
	}
}

void ggt_impl::helloChef() {
	//Say Hello to Chef
	cout << myPIDisMyName;
	bigBoss->hello(myPIDisMyName);
}

/* initial */
/**
 * Der Koordinator teilt einem ggT-Prozess seinen
 * linken (pidleft) und
 * rechten (pidright) Nachbarn des logischen Ringes mit,
 * indem er die entsprechenden Nummeren der Nachbarn
 * (ohne Zusatz slave) mitteilt. Als Rückgabewert erhält er
 *  einen Fehlercode, mit 1 = OK und 0 = error.
 */
long ggt_impl::setneighbors(long pidleft, long pidright) {
	cout<<"left"<<pidleft<<" right"<<pidright<<endl;
	this->logger << "setneighbors left:" << pidleft << " right:" << pidright << endl;

	char myPLeft[11];
	sprintf(myPLeft, "slave%5d", (int)pidleft);
	name[0].id = CORBA::string_dup (myPLeft);
	name[0].kind = CORBA::string_dup ("");
	obj = nc->resolve(name);
	leftNeighbor = ggt::unit::_narrow(obj);

	char myPRight[11];
	sprintf(myPRight, "slave%5d", (int)pidright);
	name[0].id = CORBA::string_dup (myPRight);
	name[0].kind = CORBA::string_dup ("");
	obj = nc->resolve(name);
	rightNeighbor = ggt::unit::_narrow(obj);

	return 1;
}

/**
 * Der Koordinator teilt einem ggT-Prozess seine interne Zahl Mi (pm) mit.
 */
void ggt_impl::setpm(long pm) {
	gotContact();
	cout<<"PM "<<pm<<endl;
	this->logger << "set pm=" << pm << endl;
	mi = pm;
}

/* rechnen */
/**
 * Ein ggT-Prozess (oder beim Start der Koordinator) sendet
 * die Zahl Mi (y) an einen (benachbarten) ggT-Prozess.
 */
void ggt_impl::sendy(long y) {
	rechnen = true;
	bereit = false;
	gotContact();
	cout<<"Y "<<y<<endl;
	time(&sysZeit);
	this->logger << &sysZeit << "send y=" << y << " received" << endl;
	//########################################################################
	//Loesung zu Punkt 22
	//########################################################################
	if (y<mi) {
		//####################################################################
		//Loesung zu Punkt 24
		//####################################################################
		mi = ((mi - 1)% y)+1;
		this->logger << "new mi=" << mi << endl;

		sleep(sleepingtime);

		//####################################################################
		//Loesung zu Punkt 23
		//####################################################################

		bigBoss->brief(myPIDisMyName, mi, getTime());
		leftNeighbor->sendy(mi);
		rightNeighbor->sendy(mi);
	}
}

/**
 * Ein ggT-Prozess hat den **-timeout festgestellt und befragt seinen rechten
 * Nachbarn, ob er mit der Terminierung einverstanden ist.
 */
//Anforderung 26
void ggt_impl::query(long pid) {
	cout << "Query" << endl;
	this->logger << "Query received" << endl;
//Anfordrung 26.2. (1->ja)
	if(pid == myPIDisMyName){ //ich war initiator
		leftNeighbor->response(1);
//Anforderung 26.1.
	}else{
		time(&sysZeit);
		if(sysZeit < lifeTime - (processTimeout/2)){
		      leftNeighbor->response(0);
		}else{
		      rightNeighbor->query(pid);
		}
	}
}

/**
 * Ein ggT-Prozess beantwortet die Frage nach der Terminierung
 * mit 0 für Nein und 1 für Ja.
 */
void ggt_impl::response(long y) {
	this->logger << "Response received " << y << endl;

	if(y == 1){
		// wenn terminiert sollen keine responses mehr durchgereicht werden
		if(!bereit){
			this->logger << "Terminated" << endl;
			cout << "Terminated" << endl;
			bigBoss->terminated(myPIDisMyName, mi, getTime());
		
			rechnen = false;
			bereit = true;
			leftNeighbor->response(1);
		}
		
	}else{
		leftNeighbor->response(0);
	}

}

/* beenden */
/**
 * Der Koordinator (oder im Fehlerfall ein ggT-Prozess) sendet
 * einem ggT-Prozess die Aufforderung, sich zu beenden.
 */
void ggt_impl::close() {
	cout << "Close" << endl;
	this->logger << "Close" << endl;
	bigBoss->closed(myPIDisMyName, mi, getTime());
	kill = true;
}

long ggt_impl::getTime() {
	time(&sysZeit);
	strftime(formattime, 7, "%H%M%S", localtime(&sysZeit));
	return atol(formattime);
}

bool ggt_impl::tryToKill(){
	bool retVal = kill;

	if(!rechnen)
		gotContact();

	time(&sysZeit);
	if(lifeTime<sysZeit){
		//initiatedQuery = true;
		rightNeighbor->query(myPIDisMyName);
	}
	return retVal;
}

void ggt_impl::gotContact(){
	time(&lifeTime);
	lifeTime += processTimeout;  // = timeOfDeath
}

ggt_impl::~ggt_impl() {

}

